package edu.arizona.sista.learn.activity.fluents.paths;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Scene {
	private int numFrames;
	private Vector<SceneEntity> entities;
	
	public Scene(int numFrames) {
		this.numFrames = numFrames;
		this.entities = new Vector<SceneEntity>(numFrames);
	}
	
	public static Scene load(String path) throws IOException {
		return load(path, 1);
	}
	
	public static Scene load(String path, int conf) throws IOException {
		Scene scene = null;
		File dir = new File(path);
		if (!dir.isDirectory()) {
			throw new IOException();
		}
		if (conf == 1) {
			File[] listOfFiles = getFrames(dir);
			scene = new Scene(listOfFiles.length);
			for (int i = 0; i < listOfFiles.length; i++) {
				if (listOfFiles[i].isFile()) {
					File f = listOfFiles[i];
					BufferedReader br = new BufferedReader(new FileReader(f));
					String line;
					while ( (line = br.readLine()) != null) {
						String[] data = line.split("\\s+");
						SceneEntity e = scene.getOrSetSceneEntity(data[0], data[1]);
						e.setPoint(i, new Point3D(data[2], data[3], data[4]));
						if (data[5].compareTo(".") != 0 && data[6].compareTo(".") != 0) {
							SceneEntity e2 = scene.getOrSetSceneEntity(data[5], data[6]);
							e.attach(i, e2);
						}
					}
					br.close();
				}
			}
		} else if (conf == 2) {
			File[] listOfFiles = dir.listFiles(new FilenameFilter() {
				public boolean accept(File arg0, String arg1) {
					return arg1.endsWith(".txt");
				}
			});
			if (listOfFiles.length < 1)
				return null;
			scene = new Scene(getNumLines(listOfFiles[0]));
			for (int i = 0; i < listOfFiles.length; i++) {
				if (listOfFiles[i].isFile()) {
					File f = listOfFiles[i];
					BufferedReader br = new BufferedReader(new FileReader(f));
					String line;
					String[] data = f.getName().replace(".txt", "").split("_");
					assert data.length == 2;
					SceneEntity e = scene.getOrSetSceneEntity(data[0], data[1]);
					int frame = 0;
					while ( (line = br.readLine()) != null) {
						data = line.trim().split("\\s+");
						if (!data[3].trim().startsWith("0")) {
							e.setPoint(frame, new Point3D(data[0], data[1], data[2]));
						}
						frame++;
					}
					br.close();
				}
			}
		} else {
			File[] listOfFiles = dir.listFiles(new FilenameFilter() {
				public boolean accept(File arg0, String arg1) {
					return arg1.startsWith("person") && arg1.endsWith(".txt");
				}
			});
			if (listOfFiles.length < 1)
				return null;
			scene = new Scene(getNumLines(listOfFiles[0]) - 1);
			for (int i = 0; i < listOfFiles.length; i++) {
				if (listOfFiles[i].isFile()) {
					File f = listOfFiles[i];
					BufferedReader br = new BufferedReader(new FileReader(f));
					String line = br.readLine();	// skip first line
					String[] data = f.getName().replace(".txt", "").split("_");
					assert data.length == 2;
					SceneEntity e = scene.getOrSetSceneEntity(data[0], data[1]);
					int frame = 0;
					while ( (line = br.readLine()) != null) {
						data = line.trim().split("\\s+");
						if (!data[6].trim().startsWith("0")) {
							e.setPoint(frame, new Point3D(data[0], data[1], data[2]));
						}
						frame++;
					}
					br.close();
				}
			}
		}
		return scene;
	}
	
	private static int getNumLines(File file) throws IOException {
		InputStream is = new BufferedInputStream(new FileInputStream(file));
		try {
			byte[] c = new byte[1024];
			int count = 0;
			int readChars = 0;
			while ((readChars = is.read(c)) != -1) {
				for (int i = 0; i < readChars; ++i) {
					if (c[i] == '\n')
						++count;
				}
			}
			return count;
		} finally {
			is.close();
		}
	}
	
	private static File[] getFrames(File dir) {
		File[] listOfFiles = dir.listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return Pattern.matches("^(\\d+)\\.txt$", name);
			}
		});
		
		Arrays.sort(listOfFiles, new Comparator<File>() {
			public int compare(File f1, File f2) {
				Pattern pattern = Pattern.compile("(\\d+)\\.txt$");
				Matcher m1 = pattern.matcher(f1.getName());
				Matcher m2 = pattern.matcher(f2.getName());
				m1.find();
				m2.find();
				int i1 = Integer.parseInt(m1.group(1));
				int i2 = Integer.parseInt(m2.group(1));
				return i1 - i2;
			}
		});
		
		return listOfFiles;
	}
	
	@Override
	public String toString() {
		return "<Scene(" + getNumFrames() + " frames)>";
	}
	
	public int getNumFrames() {
		return numFrames;
	}
	
	public Vector<SceneEntity> getEntities() {
		return entities;
	}
	
	private SceneEntity getOrSetSceneEntity(String type, String id) {
		return getOrSetSceneEntity(type, Integer.parseInt(id));
	}
	
	private SceneEntity getOrSetSceneEntity(String type, int id) {
		for (SceneEntity e : entities) {
			if (e.getType().equals(type) && e.getId() == id) {
				return e;
			}
		}
		SceneEntity e = new SceneEntity(type, id, getNumFrames());
		entities.add(e);
		return e;
	}
}
